package pku.serializers;

import io.Snappy;
import pku.DefaultMessage;
import pku.KeyValue;
import pku.Utils;
import pku.constant.MessageHeaderMap;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public final class OldMessageSerializer {

    public static byte[] message2bytes(DefaultMessage message) {
        KeyValue headers = message.headers();
        byte[] body = message.getBody();
        byte[] headersBytes = keyValue2bytes(headers);
        int msgLen = headersBytes.length + body.length;
        if (msgLen > 256) {
            byte[] comData = Snappy.compress(Utils.byteMergerAll(headersBytes, body));
            int comLen = comData.length;
            byte[] comLenBytes = Utils.int2bytes3(comLen);
            comLenBytes[2] |= 0x80;
            return Utils.byteMergerAll(comLenBytes, comData);
        } else {
            byte[] msgLenBytes = Utils.int2bytes3(msgLen);
            return Utils.byteMergerAll(msgLenBytes, headersBytes, body);
        }
    }

    private static byte[] keyValue2bytes(KeyValue kvs) {
        Set<String> keySet = kvs.keySet();
        List<Byte> headersBytes = new ArrayList<>();
        int cnt = 0;
        int keyType = 0;
        for (String k : MessageHeaderMap.headerKeys) {
            if (keySet.contains(k)) {
                keyType |= (1 << cnt);
                if (cnt < 4) {
                    // int
                    int val = kvs.getInt(k);
                    byte[] valBytes = Utils.int2bytes(val);
                    for (byte b : valBytes) headersBytes.add(b);
                } else if (cnt < 8) {
                    long val = kvs.getLong(k);
                    byte[] valBytes = Utils.long2bytes(val);
                    for (byte b : valBytes) headersBytes.add(b);
                } else if (cnt < 10) {
                    double val = kvs.getDouble(k);
                    byte[] valBytes = Utils.double2bytes(val);
                    for (byte b: valBytes) headersBytes.add(b);
                } else{
                    byte[] valBytes = kvs.getString(k).getBytes();
                    int valLen = valBytes.length;
                    byte[] valLenBytes = Utils.int2bytes2(valLen);
                    for (byte b : valLenBytes) headersBytes.add(b);
                    for (byte b : valBytes) headersBytes.add(b);
                }
            }
            cnt++;
        }
        int len = headersBytes.size();
        byte[] headers = new byte[len + 2];
        byte[] kt = Utils.int2bytes2(keyType);
//        System.arraycopy(kt, 0, headers, 0, 2);
        headers[0] = kt[0];
        headers[1] = kt[1];
        for (int i = 0; i < len; i++) {
            headers[i + 2] = headersBytes.get(i);
        }
        return headers;
    }

    public static DefaultMessage bytes2message(byte[] msg) {
        DefaultMessage message = new DefaultMessage();
        byte[] keyTypeCode = new byte[2];
        keyTypeCode[0] = msg[0];
        keyTypeCode[1] = msg[1];
        int curIndex = 2;
        int msgLen = msg.length;
        int keyType = Utils.bytes2int2(keyTypeCode);
        for (int i = 0; i < 15; i++) {
            if (((keyType >> i) & 0x01) == 0x01) {
                String keyName = MessageHeaderMap.headerKeys[i];
                if (i < 4) {
                    byte[] val = new byte[4];
                    System.arraycopy(msg, curIndex, val, 0, 4);
                    curIndex += 4;
                    message.putHeaders(keyName, Utils.bytes2int(val));
                } else if (i < 8) {
                    byte[] val = new byte[8];
                    System.arraycopy(msg, curIndex, val, 0, 8);
                    curIndex += 8;
                    message.putHeaders(keyName, Utils.bytes2long(val));
                } else if (i < 10) {
                    byte[] val = new byte[8];
                    System.arraycopy(msg, curIndex, val, 0, 8);
                    curIndex += 8;
                    message.putHeaders(keyName, Utils.bytes2double(val));
                } else {
                    byte[] strLen = new byte[2];
                    strLen[0] = msg[curIndex++];
                    strLen[1] = msg[curIndex++];
                    int len = Utils.bytes2int2(strLen);
                    byte[] strBytes = new byte[len];
                    System.arraycopy(msg, curIndex, strBytes, 0, len);
                    curIndex += len;
                    message.putHeaders(keyName, new String(strBytes));
                }
            }
        }
        byte[] body = new byte[msgLen - curIndex];
        System.arraycopy(msg, curIndex, body, 0, msgLen - curIndex);
        message.setBody(body);
        return message;
    }
}